#include "rtda/heap/exception_table.h"
#include "rtda/heap/constant_pool.hpp"
#include "rtda/heap/class.h"

namespace jvm {
    namespace heap {

        std::shared_ptr<ClassRef> getCatchType(uint32_t index, std::shared_ptr<heap::ConstantPool> pCp)
        {
            if (0 == index) return nullptr;

            auto&& constant = pCp->getConstant(index);
            if (!std::holds_alternative<std::shared_ptr<ClassRef>>(constant))
            {
                LOG_ERROR("Invalid constant type: expected ClassRef Pointer");
                throw std::runtime_error("Invalid constant type: expected ClassRef Pointer");
            }
            return std::get<std::shared_ptr<ClassRef>>(constant);
        }

        ExceptionTable::ExceptionTable(
            const std::vector<std::shared_ptr<classfile::ExceptionTableEntry>>& entries,
            std::shared_ptr<heap::ConstantPool> pCp)
        {
            _handlers.reserve(entries.size());
            for (const auto& pEntry : entries) {
                auto pHandler = std::make_shared<ExceptionHandler>();
                pHandler->startPc = static_cast<int>(pEntry->getStartPc());
                pHandler->endPc = static_cast<int>(pEntry->getEndPc());
                pHandler->handlerPc = static_cast<int>(pEntry->getHandlerPc());
                pHandler->pCatchType = getCatchType(pEntry->getCatchType(), pCp);
                _handlers.push_back(pHandler);
            }
        }

        std::shared_ptr<ExceptionHandler> ExceptionTable::findExceptionHandler(
            std::shared_ptr<Class> pExClass, int pc) const 
        {
            if (!pExClass)
            {
                LOG_ERROR("null pointer!");
                throw std::runtime_error("null pointer!");
            }

            LOG_DEBUG("ExceptionTable size: " << _handlers.size());
            for (auto& pHandler : _handlers) {
                if (pc >= pHandler->startPc && pc < pHandler->endPc)
                {
                    if (pHandler->pCatchType == nullptr) {
                        return pHandler; // catch-all
                    }

                    auto pCatchClass = pHandler->pCatchType->resolvedClass();
                    if (!pCatchClass)
                    {
                        LOG_ERROR("null pointer!");
                        throw std::runtime_error("null pointer!");
                    }
                    if (pCatchClass == pExClass || pCatchClass->isSuperClassOf(pExClass)) {
                        return pHandler;
                    }
                }
            }
            return nullptr;
        }

    } // namespace heap
} // namespace jvm